<?php
/**
 * utils
 * 
 * @author tom.wang<tom.wang.sz@qq.com>
 */
class RenrenUtil {
	public static function http($url, $method, $postfields = array(), $gzip = false, $multi = false) {
		$call = RenrenClient::getCfg('call');
		$method = 'http' . ucfirst(strtolower($call));
		$args = func_get_args();
		return call_user_func_array(array(__CLASS__, $method), $args);
	}
	
	/**
	 * http request(curl)
	 */
	private static function httpCurl($url, $method, $postfields = array(), $gzip = false, $multi = false) {
		static $ch = null;
		if(null === $ch) $ch = curl_init();
		$curlOpts = array(
			CURLOPT_CONNECTTIMEOUT 	=> 10,
			CURLOPT_RETURNTRANSFER 	=> true,
			CURLOPT_AUTOREFERER		=> true,
			CURLOPT_FOLLOWLOCATION	=> true,
			CURLOPT_SSL_VERIFYPEER	=> false,
			CURLOPT_TIMEOUT        	=> 60,
			CURLOPT_USERAGENT      	=> 'renren_php_api',
			CURLOPT_URL				=> $url,
			# disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
		    # for 2 seconds if the server does not support this header.
			CURLOPT_HTTPHEADER 		=> array('Expect:'),
		);
		
		foreach($postfields as $key => $val) {
			if('@' === substr($val, 0, 1) && is_file(substr($val, 1))) {
				$method = 'post';
				$multi = true;
				break;
			}
		}
		
		# multi-part
		if('POST' === strtoupper($method)) {
			if($multi) {
				# if you want upload a file, use @ at the value of $postfields's values
				$curlOpts[CURLOPT_POSTFIELDS] = $postfields;
			} else {
				$curlOpts[CURLOPT_POSTFIELDS] = http_build_query($postfields, null, '&');
			}
		}
		
		# gzip
		if($gzip) {
			$curlOpts[CURLOPT_HTTPHEADER][] = 'Accept-Encoding: gzip';
		}
		curl_setopt_array($ch, $curlOpts);
	    $result = curl_exec($ch);
		curl_close($ch);
		if(false === $result) {
			RenrenException::throwEx('curl fail');
		} else {
			$gzip && $result = self::gzdecode($result);
			return $result;
		}
	}
	/**
	 * http request(fsockopen)
	 */
	private static function httpFsockopen($url, $method, $postfields = array(), $gzip = false, $multi = false){
		$urlArr = parse_url($url);
		//check https
		$version = '1.1';
		$host = $headerHost = isset($urlArr['host']) ? $urlArr['host'] : '';
		if('https' === strtolower($urlArr['scheme'])) {
			$port = (isset($urlArr['port']) && '443' !== $urlArr['port']) ? $urlArr['port'] : '443';
			$host = "ssl://{$host}";
		} else {
			$port = (isset($urlArr['port']) && '80' !== $urlArr['port']) ? $urlArr['port'] : '80';
		}
		
		$requestPath = $urlArr['path'];
		if(isset($urlArr['query']) && !empty($urlArr['query'])) $requestPath .= "?{$urlArr['query']}";
	
		$header = "{$method} {$requestPath} HTTP/{$version}\r\n";
		$header .= "Host: {$headerHost}\r\n";
		$gzip && $header .= "Accept-Encoding: gzip\r\n";
		foreach($postfields as $key => $val) {
			if(is_file($val)) {
				$method = 'post';
				$multi = true;
				break;
			}
		}
		if('post' === strtolower($method)) {
			//handle $postfields
			$postfieldsStr = '';
			if($multi) {
				$boundary = "---------------------------".substr(md5(rand(0,32000)),0,10);
				$header .= "Content-Type: multipart/form-data; boundary={$boundary}\r\n";
				foreach($postfields as $key => $val) {
					$postfieldsStr .= "--{$boundary}\r\n";
					if(is_file($val)) {
						$postfieldsStr .= "Content-Disposition: form-data; name=\"{$key}\"; filename=\"" . basename($val) . "\"\r\n";
						$postfieldsStr .= "Content-Type: " . self::getMimeType($val) . "\r\n\r\n";
						$postfieldsStr .= file_get_contents($val) . "\r\n";
					} else {
						$postfieldsStr .= "Content-Disposition: form-data; name=\"{$key}\"\r\n\r\n";
						$postfieldsStr .= "{$val}\r\n";
					}
				}
				
				$postfieldsStr .= "--{$boundary}--\r\n";
			} else {
				$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
				foreach($postfields as $key => $val) {
					$postfieldsStr .= urlencode($key) . '=' . urlencode($val) . '&';
				}
				$postfieldsStr = substr($postfieldsStr, 0, -1);
			}
			
			$header .= "Content-Length: ".strlen($postfieldsStr)."\r\n";
			$header .= "Connection: Close\r\n\r\n";
			$header .= $postfieldsStr;
		} else {
			$header .= "Connection: Close\r\n\r\n";
		}
	
		$ret = '';
		
		$fp = fsockopen($host,$port,$errno,$errstr,30);
	
		if(false === $fp) {
			RenrenException::throwEx('open socket fail');
		} else {
			fwrite($fp, $header);
			while(!feof($fp)) {
				$ret .= fgets($fp, 4096);
			}
			fclose($fp);
			if(false !== strrpos($ret,'Transfer-Encoding: chunked')){
				$info = explode("\r\n\r\n",$ret);
				$response = explode("\r\n",$info[1]);
				$t = array_slice($response,1,-1);
	
				$returnInfo = implode('',$t);
			}else{
				$response = explode("\r\n\r\n",$ret);
				$returnInfo = $response[1];
			}
			$gzip && $returnInfo = self::gzdecode($returnInfo);
			return $returnInfo;
		}
	}
	
	/**
	 * get mime type
	 */
	public static function getMimeType($filename) {
		$mime_types = array(
			'txt' => 'text/plain',
			'htm' => 'text/html',
			'html' => 'text/html',
			'php' => 'text/html',
			'css' => 'text/css',
			'js' => 'application/javascript',
			'json' => 'application/json',
			'xml' => 'application/xml',
			'swf' => 'application/x-shockwave-flash',
			'flv' => 'video/x-flv',
			
			// images
			'png' => 'image/png',
			'jpe' => 'image/jpeg',
			'jpeg' => 'image/jpeg',
			'jpg' => 'image/jpeg',
			'gif' => 'image/gif',
			'bmp' => 'image/bmp',
			'ico' => 'image/vnd.microsoft.icon',
			'tiff' => 'image/tiff',
			'tif' => 'image/tiff',
			'svg' => 'image/svg+xml',
			'svgz' => 'image/svg+xml',
			
			// archives
			'zip' => 'application/zip',
			'rar' => 'application/x-rar-compressed',
			'exe' => 'application/x-msdownload',
			'msi' => 'application/x-msdownload',
			'cab' => 'application/vnd.ms-cab-compressed',
			
			// audio/video
			'mp3' => 'audio/mpeg',
			'qt' => 'video/quicktime',
			'mov' => 'video/quicktime',
			
			// adobe
			'pdf' => 'application/pdf',
			'psd' => 'image/vnd.adobe.photoshop',
			'ai' => 'application/postscript',
			'eps' => 'application/postscript',
			'ps' => 'application/postscript',
			
			// ms office
			'doc' => 'application/msword',
			'rtf' => 'application/rtf',
			'xls' => 'application/vnd.ms-excel',
			'ppt' => 'application/vnd.ms-powerpoint',
			
			// open office
			'odt' => 'application/vnd.oasis.opendocument.text',
			'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
		);
		
		$ext = strtolower(array_pop(explode('.',$filename)));
		if (array_key_exists($ext, $mime_types)) {
			return $mime_types[$ext];
		} elseif (function_exists('finfo_open')) {
			$finfo = finfo_open(FILEINFO_MIME);
			$mimetype = finfo_file($finfo, $filename);
			finfo_close($finfo);
			return $mimetype;
		} else {
			return 'application/octet-stream';
		}
	}
	
	/**
	 * get signature
	 */
	public static function getSig($params) {
		array_walk($params, array(__CLASS__, 'concatParams'));
		$params = array_values($params);
		sort($params);
		$sig = implode('', $params);
		$sig .= RenrenClient::getCfg('oauth_consumer_token_secret');
		return md5($sig);
	}
	
	private static function concatParams(&$val, $key) {
		$val = "{$key}={$val}";
	}
	
	private static function gzdecode($data,&$filename='',&$error='',$maxlength=null) {
		$len = strlen($data);
		if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
			$error = "Not in GZIP format.";
			return null;  // Not GZIP format (See RFC 1952)
		}
		$method = ord(substr($data,2,1));  // Compression method
		$flags  = ord(substr($data,3,1));  // Flags
		if ($flags & 31 != $flags) {
			$error = "Reserved bits not allowed.";
			return null;
		}
		// NOTE: $mtime may be negative (PHP integer limitations)
		$mtime = unpack("V", substr($data,4,4));
		$mtime = $mtime[1];
		$xfl   = substr($data,8,1);
		$os    = substr($data,8,1);
		$headerlen = 10;
		$extralen  = 0;
		$extra     = "";
		if ($flags & 4) {
			// 2-byte length prefixed EXTRA data in header
			if ($len - $headerlen - 2 < 8) {
				return false;  // invalid
			}
			$extralen = unpack("v",substr($data,8,2));
			$extralen = $extralen[1];
			if ($len - $headerlen - 2 - $extralen < 8) {
				return false;  // invalid
			}
			$extra = substr($data,10,$extralen);
			$headerlen += 2 + $extralen;
		}
		$filenamelen = 0;
		$filename = "";
		if ($flags & 8) {
			// C-style string
			if ($len - $headerlen - 1 < 8) {
				return false; // invalid
			}
			$filenamelen = strpos(substr($data,$headerlen),chr(0));
			if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
				return false; // invalid
			}
			$filename = substr($data,$headerlen,$filenamelen);
			$headerlen += $filenamelen + 1;
		}
		$commentlen = 0;
		$comment = "";
		if ($flags & 16) {
			// C-style string COMMENT data in header
			if ($len - $headerlen - 1 < 8) {
				return false;    // invalid
			}
			$commentlen = strpos(substr($data,$headerlen),chr(0));
			if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
				return false;    // Invalid header format
			}
			$comment = substr($data,$headerlen,$commentlen);
			$headerlen += $commentlen + 1;
		}
		$headercrc = "";
		if ($flags & 2) {
			// 2-bytes (lowest order) of CRC32 on header present
			if ($len - $headerlen - 2 < 8) {
				return false;    // invalid
			}
			$calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
			$headercrc = unpack("v", substr($data,$headerlen,2));
			$headercrc = $headercrc[1];
			if ($headercrc != $calccrc) {
				$error = "Header checksum failed.";
				return false;    // Bad header CRC
			}
			$headerlen += 2;
		}
		// GZIP FOOTER
		$datacrc = unpack("V",substr($data,-8,4));
		$datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF);
		$isize = unpack("V",substr($data,-4));
		$isize = $isize[1];
		// decompression:
		$bodylen = $len-$headerlen-8;
		if ($bodylen < 1) {
			// IMPLEMENTATION BUG!
			return null;
		}
		$body = substr($data,$headerlen,$bodylen);
		$data = "";
		if ($bodylen > 0) {
			switch ($method) {
			case 8:
				// Currently the only supported compression method:
				$data = gzinflate($body,$maxlength);
				break;
			default:
				$error = "Unknown compression method.";
				return false;
			}
		}  // zero-byte body content is allowed
		// Verifiy CRC32
		$crc   = sprintf("%u",crc32($data));
		$crcOK = $crc == $datacrc;
		$lenOK = $isize == strlen($data);
		if (!$lenOK || !$crcOK) {
			$error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.');
			return false;
		}
		return $data;
	}
}
?>